// @version=4
// @author=andre_007
// Copyright (c) 2021 André Eduardo Pérez Álvarez
// 
// This sourcecode is released under the MIT License.
// https://opensource.org/licenses/MIT
//
// Many thanks to: 
//      Alex Orekhov (everget)
//      JacobAmos
//      lonestar108
//      HPotter
//      surjithctly
//      TradingView
//
// Links:
//      https://br.tradingview.com/script/eubdyhzk-Stochastic-Momentum-Index/
//      https://br.tradingview.com/script/NULg8CIG-Stochastic-Momentum-Index/
//      https://br.tradingview.com/script/HLbqdCku-Stochastic-Momentum-Index-SMI/
//      https://br.tradingview.com/script/KvJKGi0C-SMI-Ergodic-Oscillator-Strategy-ver-2/
//      https://br.tradingview.com/script/TYB9LoiW-SMI-Bars/
//      
//  
//  Description
//      This is as customized version of "Stochastic Momentum Index" (SMI) "Ergodic".
//
//      Basically, I took a lot of interesting ideas from the authors mentioned above, and put them all together.
//      Implementations:
//        
//      1. Script developed in PineScript 4
//      2. Possibility to choose two themes
//      3. Possibility to choose the type of moving average applied to both the SMI indicator and the signal
//      4. Histogram with gradient colors, emphasizing the strength of momentum
//      5. Crossover and crossunder signs
//      6. Coloring of the bars like the histogram
//      7. Background fill between indicator and signal lines
//
//      By default, the SMI and Signal lines uses the Exponential Moving Average (EMA).
//
//      In this script, the following moving averages can also be used:
//      - Simple (SMA)
//      - Double Exponential (DEMA)
//      - Triple Exponential (THEME)
//      - Weighted (WMA)
//      - Smoothed (SSMA)
//      - Least Squares (LSMA)
//      - Hull
//      - Arnaud Legoux
//
//      There are 256 possibilities...

study("Stochastic Momentum Index (SMI) Ergodic [andre_007]", shorttitle="SMI", resolution="")

//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
// -- Levels
float LEVEL_STRONG_1 = 5
float LEVEL_STRONG_2 = 10
float LEVEL_STRONG_3 = 15
float LEVEL_STRONG_4 = 20
float LEVEL_STRONG_5 = 25

float LEVEL_WEAK_1 = -5
float LEVEL_WEAK_2 = -10
float LEVEL_WEAK_3 = -15
float LEVEL_WEAK_4 = -20
float LEVEL_WEAK_5 = -25

// -- Colors
color C_LEVEL_STRONG_5 = #228B22
color C_LEVEL_STRONG_4 = #2BB02B
color C_LEVEL_STRONG_3 = #3ACF3A
color C_LEVEL_STRONG_2 = #5FD85F
color C_LEVEL_STRONG_1 = #84E184

color C_LEVEL_WEAK_5 = #6C0BA9
color C_LEVEL_WEAK_4 = #880ED4
color C_LEVEL_WEAK_3 = #A020F0
color C_LEVEL_WEAK_2 = #B24BF3
color C_LEVEL_WEAK_1 = #C576F6

color C_TRANSPARENT = color.new(color.white, 100)

color C_OVERSOLD_AREA = color.new(#228B22, 70)
color C_OVERBOUGHT_AREA = color.new(#A020F0, 70)

color C_OVERSOLD_SIGNAL = color.new(color.lime, 70)
color C_OVERBOUGHT_SIGNAL = color.new(color.fuchsia, 70)

color C_TREND_BULL = color.new(color.green, 30)
color C_TREND_BEAR = color.new(color.purple, 30)

color C_SMI = color.new(#ff3e7d, 0)
color C_SIGNAL = color.new(#3c78d8, 0)

color C_CROSSOVER = color.new(color.blue, 0)
color C_CROSSUNDER = color.new(color.red, 0)

// -- Type of Moving Averages
string SMA  = "Simple"
string EMA  = "Exponential"
string DEMA = "Double Exponential"
string TEMA = "Triple Exponential"
string WMA  = "Weighted"
string SMMA = "Smoothed"
string LSMA = "Least Squares"
string HMA  = "Hull"
string ALMA = "Arnaud Legoux"

string THEME_DEEP_PURPLE = "Deep Purple"
string THEME_BLUE_RED = "Blue and Red"

//------------------------------------------------------------------------------
// Functions
//------------------------------------------------------------------------------
f_color_histogram(value) =>
    color _color = na    

    if value >= LEVEL_STRONG_5
        _color := C_LEVEL_STRONG_5
    else if value >= LEVEL_STRONG_4
        if value > value[1]
            _color := C_LEVEL_STRONG_5
        else
            _color := C_LEVEL_STRONG_4
    else if value >= LEVEL_STRONG_3
        if value > value[1]
            _color := C_LEVEL_STRONG_4
        else
            _color := C_LEVEL_STRONG_3    
    else if value >= LEVEL_STRONG_2
        if value > value[1]
            _color := C_LEVEL_STRONG_3
        else
            _color := C_LEVEL_STRONG_2
    else if value > 0        
        if value > value[1]
            _color := C_LEVEL_STRONG_2
        else
            _color := C_LEVEL_STRONG_1
    else if value <= LEVEL_WEAK_5
        _color := C_LEVEL_WEAK_5
    else if value <= LEVEL_WEAK_4
        if value < value[1]
            _color := C_LEVEL_WEAK_5
        else
            _color := C_LEVEL_WEAK_4
    else if value <= LEVEL_WEAK_3
        if value < value[1]
            _color := C_LEVEL_WEAK_4
        else
            _color := C_LEVEL_WEAK_3
    else if value <= LEVEL_WEAK_2
        if value < value[1]
            _color := C_LEVEL_WEAK_3
        else
            _color := C_LEVEL_WEAK_2
    else
        if value < value[1]
            _color := C_LEVEL_WEAK_2
        else
            _color := C_LEVEL_WEAK_1

f_sma(_src, _len) =>
    out = sma(_src, _len)

f_ema(_src, _len) =>
    out = ema(_src, _len)

f_dema(_src, _len) =>
    e1 = ema(_src, _len)
    e2 = ema(e1, _len)
    out = 2 * e1 - e2

f_tema(_src, _len) =>
    ema1 = ema(_src, _len)
    ema2 = ema(ema1, _len)
    ema3 = ema(ema2, _len)
    out = 3 * (ema1 - ema2) + ema3

f_wma(_src, _len) =>
    out = wma(_src, _len)

f_smma(_src, _len) =>
    smma = 0.0
    smma := na(smma[1]) ? sma(_src, _len) : (smma[1] * (_len - 1) + _src) / _len

f_lsma(_src, _len) =>
    out = linreg(_src, _len, 0)

f_hullma(_src, _len) =>
    out = wma(2*wma(_src, _len/2)-wma(_src, _len), floor(sqrt(_len)))

f_alma(_src, _len, _alma_offset, _alma_sigma) =>
    out = alma(_src, _len, _alma_offset, _alma_sigma)    

f_average(_type_ma, _src, _len, _alma_offset, _alma_sigma) =>
    if _type_ma == SMA
        f_sma(_src, _len)
    else if _type_ma == EMA
        f_ema(_src, _len)
    else if _type_ma == DEMA
        f_dema(_src, _len)
    else if _type_ma == TEMA
        f_tema(_src, _len)
    else if _type_ma == WMA
        f_wma(_src, _len)
    else if _type_ma == SMMA
        f_smma(_src, _len)      
    else if _type_ma == LSMA
        f_lsma(_src, _len)
    else if _type_ma == HMA
        f_hullma(_src, _len)
    else if _type_ma == ALMA
        f_alma(_src, _len, _alma_offset, _alma_sigma)    
        
//------------------------------------------------------------------------------
// Inputs
//------------------------------------------------------------------------------
src = input(title="Source", type=input.source, defval=close)

string G_GROUP_SMI = "Calculation parameters"
length = input(title="Stochastic Lookback", type=input.integer, defval=13, minval=1, group=G_GROUP_SMI)
smoth_len_1 = input(title="1st Smoothing Length", type=input.integer, defval=25, minval=1, group=G_GROUP_SMI)
smoth_len_2 = input(title="2nd Smoothing Length", type=input.integer, defval=2, minval=1, group=G_GROUP_SMI)
signal_Length = input(title="Signal Smoothing Length", type=input.integer, defval=13, minval=1, group=G_GROUP_SMI)

string G_GROUP_MA = "Moving Averages"
type_ma_smi = input(EMA, "Moving Average Type for SMI", options=[SMA, EMA, DEMA, TEMA, WMA, SMMA, LSMA, HMA, ALMA], group=G_GROUP_MA)
type_ma_signal = input(EMA, "Moving Average Type for Signal", options=[SMA, EMA, DEMA, TEMA, WMA, SMMA, LSMA, HMA, ALMA], group=G_GROUP_MA)

var string GROUP_ALMA = "Exclusive for Arnaud Legoux Moving Average (ALMA)"
alma_offset = input(title="Offset", type=input.float, defval=0.85, group=GROUP_ALMA, inline="04", tooltip="This offset is considered in the formula, not in the graphic! Offset is the Gaussian applied to the combo line and it is 0.85 by default. Setting offset at 1 makes it fully aligned to the current price just like the exponential moving average. While setting it to zero makes it just like a simple moving average. Traders may try offset combinations according to their own needs and preferences.")
alma_sigma = input(title="Sigma", type=input.float, defval=6, group=GROUP_ALMA, inline="04", tooltip="The standard deviation applied to the combo line. It makes the combo line sharper.")

string G_GROUP_LEVELS = "Levels"
obLevel = input(title="Overbought Level", type=input.integer, defval=40, group=G_GROUP_LEVELS)
osLevel = input(title="Oversold Level", type=input.integer, defval=-40, group=G_GROUP_LEVELS)
maxLevel = input(title="Max Level", type=input.integer, defval=100, group=G_GROUP_LEVELS)
minLevel = input(title="Min Level", type=input.integer, defval=-100, group=G_GROUP_LEVELS)

string G_EXTRAS = "Extras"
showHistogram = input(title="Show Histogram ?", type=input.bool, defval=true, group=G_EXTRAS)
highlightCrossovers = input(title="Highlight Indicator/Signal Crossovers ?", type=input.bool, defval=true, group=G_EXTRAS)
highlighBackground = input(title="Highlight background on Overbought/Oversold Signal ?", type=input.bool, defval=true, group=G_EXTRAS)
applyFilling = input(title="Apply Ribbon Filling ?", type=input.bool, defval=true, group=G_EXTRAS)
applyBarColors = input(title="Apply Bar Colors ?", type=input.bool, defval=false, group=G_EXTRAS)
theme = input(THEME_DEEP_PURPLE, "Theme", options=[THEME_DEEP_PURPLE, THEME_BLUE_RED], group=G_EXTRAS)

//------------------------------------------------------------------------------
// Calcs
//------------------------------------------------------------------------------
_highest = highest(length)
_lowest = lowest(length)

_x = src - 0.5 * (_highest + _lowest)
_y = _highest - _lowest

numerator = f_average( type_ma_smi, f_average(type_ma_smi, _x, smoth_len_1, alma_offset, alma_sigma), smoth_len_2, alma_offset, alma_sigma )
denominator = 0.5 * f_average( type_ma_smi, f_average(type_ma_smi, _y, smoth_len_1, alma_offset, alma_sigma), smoth_len_2, alma_offset, alma_sigma )

smi = 100 * numerator / denominator
signal = f_average(type_ma_signal, smi, signal_Length, alma_offset, alma_sigma)
histogram = smi - signal

//------------------------------------------------------------------------------
// Dynamic Colors
//------------------------------------------------------------------------------
// -- Color's theme
C_LEVEL_STRONG_5    := (theme == THEME_BLUE_RED ? #0064bb : C_LEVEL_STRONG_5)
C_LEVEL_STRONG_4    := (theme == THEME_BLUE_RED ? #007ce9 : C_LEVEL_STRONG_4)
C_LEVEL_STRONG_3    := (theme == THEME_BLUE_RED ? #1893ff : C_LEVEL_STRONG_3)
C_LEVEL_STRONG_2    := (theme == THEME_BLUE_RED ? #46a9ff : C_LEVEL_STRONG_2)
C_LEVEL_STRONG_1    := (theme == THEME_BLUE_RED ? #75beff : C_LEVEL_STRONG_1)
C_LEVEL_WEAK_5      := (theme == THEME_BLUE_RED ? #8e1031 : C_LEVEL_WEAK_5)
C_LEVEL_WEAK_4      := (theme == THEME_BLUE_RED ? #be1642 : C_LEVEL_WEAK_4)
C_LEVEL_WEAK_3      := (theme == THEME_BLUE_RED ? #e62356 : C_LEVEL_WEAK_3)
C_LEVEL_WEAK_2      := (theme == THEME_BLUE_RED ? #eb527a : C_LEVEL_WEAK_2)
C_LEVEL_WEAK_1      := (theme == THEME_BLUE_RED ? #f1829f : C_LEVEL_WEAK_1)
C_OVERSOLD_AREA     := (theme == THEME_BLUE_RED ? color.new(color.red, 70) : C_OVERSOLD_AREA)
C_OVERBOUGHT_AREA   := (theme == THEME_BLUE_RED ? color.new(color.blue, 70) : C_OVERBOUGHT_AREA)
C_OVERSOLD_SIGNAL   := (theme == THEME_BLUE_RED ? color.new(color.red, 70) : C_OVERSOLD_SIGNAL) 
C_OVERBOUGHT_SIGNAL := (theme == THEME_BLUE_RED ? color.new(color.blue, 70) : C_OVERBOUGHT_SIGNAL)
C_TREND_BEAR        := (theme == THEME_BLUE_RED ? color.new(color.red, 30) : C_TREND_BEAR)
C_TREND_BULL        := (theme == THEME_BLUE_RED ? color.new(color.blue, 30) : C_TREND_BULL) 

// -- Indicator and oscilator
histColor = f_color_histogram(histogram)
trendColor = smi > signal ? C_TREND_BULL : C_TREND_BEAR
smiColor = applyFilling ? trendColor : C_SMI
signalColor = applyFilling ? trendColor : C_SIGNAL

//------------------------------------------------------------------------------
// Levels
//------------------------------------------------------------------------------
// -- Max\Overbought
obLevelLine = hline(obLevel, title="Overbought Level", linestyle=hline.style_dashed, color=C_TRANSPARENT)
obLevelPlot = plot(obLevel, title="Overbought Level", style=plot.style_line, color=C_TRANSPARENT)
maxLevelLine = hline(maxLevel, title="Max Level", linestyle=hline.style_dashed, color=C_TRANSPARENT)
maxLevelPlot = plot(maxLevel, title="Max Level", style=plot.style_line, color=C_TRANSPARENT)

hline(0, title="Zero Level", linestyle=hline.style_dotted, color=color.new(color.gray, 50))

// -- Min\Oversold
osLevelLine = hline(osLevel, title="Oversold Level", linestyle=hline.style_dashed, color=C_TRANSPARENT)
osLevelPlot = plot(osLevel, title="Oversold Level", style=plot.style_line, color=C_TRANSPARENT)
minLevelLine = hline(minLevel, title="Min Level", linestyle=hline.style_dashed, color=C_TRANSPARENT)
minLevelPlot = plot(minLevel, title="Min Level", style=plot.style_line, color=C_TRANSPARENT)

// -- Fills
fill(obLevelPlot, maxLevelPlot, color=C_TRANSPARENT)
fill(obLevelLine, maxLevelLine, color=C_OVERBOUGHT_AREA)
fill(osLevelPlot, minLevelPlot, color=C_TRANSPARENT)
fill(osLevelLine, minLevelLine, color=C_OVERSOLD_AREA)

//------------------------------------------------------------------------------
// Plots
//------------------------------------------------------------------------------
plot(showHistogram ? histogram : na, title="Histogram", style=plot.style_columns, color=histColor)
smiPlot = plot(smi, title="SMI", color=smiColor)
signalPlot = plot(signal, title="Signal", color=signalColor)

//-- Apply filling background between SMI and Signal line's
fillColor = applyFilling ? trendColor : C_TRANSPARENT
fill(smiPlot, signalPlot, color=fillColor)

// -- Crossover
plotshape(highlightCrossovers and crossover(smi, signal) ? signal : na, title="Crossover", location=location.absolute, style=shape.circle, size=size.tiny, color=C_CROSSOVER)
plotshape(highlightCrossovers and crossunder(smi, signal) ? signal : na, title="Crossunder", location=location.absolute, style=shape.circle, size=size.tiny, color=C_CROSSUNDER)

// -- Highlight Oversold signal region
fill(signalPlot, obLevelPlot, color=(highlighBackground and signal > obLevel ? C_OVERBOUGHT_SIGNAL : na) )
fill(signalPlot, osLevelPlot, color=(highlighBackground and signal < osLevel ? C_OVERSOLD_SIGNAL : na) )

//------------------------------------------------------------------------------
// Bar colors
//------------------------------------------------------------------------------
barcolor( applyBarColors ? histColor : na )